
/*
This file is part of MJIN2.

Copyright (C) 2012-2013 Opensource Game Studio

This software is provided 'as-is', without any express or implied
warranty.  In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/

#include <mjin2/Common.h>
#include <mjin2/InputListener.h>
#include <mjin2/priv/Input.h>

//#define MJIN2_DEBUG_INPUT

//#ifdef MJIN2_DEBUG_INPUT
//    #include <iostream>
//#endif

namespace mjin2
{
namespace priv
{

Input::Input() : mWindowHeight(0) { }

void Input::addListener(InputListener *listener) throw ()
{
    mListeners.insert(std::pair<InputListener*, bool>(listener, true));
}

bool Input::handle(const osgGA::GUIEventAdapter &ea,
                   osgGA::GUIActionAdapter      &aa,
                   osg::Object                  *o,
                   osg::NodeVisitor             *nv) throw ()
{
    // This value makes sure no other unnoticeable listener gets input.
    bool catchAll = true;
    float x = ea.getX();
    // Correct Y to go from top to bottom.
    float y = mWindowHeight - ea.getY();
    // Original (inverted) Y, useful for invoking OSG functions.
    float yi = ea.getY();
    switch (ea.getEventType())
    {
        case osgGA::GUIEventAdapter::DRAG:
        {
#ifdef MJIN2_DEBUG_INPUT
            std::cout << MJIN2_STR("mouse move (drag). x: %f y: %f\n", x, y);
#endif
            InputEvent e(INPUT_MOUSE_MOVE,
                         INPUT_MOUSE_MOVE,
                         x,
                         y,
                         yi,
                         true);
            for (auto it = mListeners.begin(); it != mListeners.end(); ++it)
                it->first->onInput(e);
            return catchAll;
        }
        case osgGA::GUIEventAdapter::KEYDOWN:
        {
#ifdef MJIN2_DEBUG_INPUT
            std::cout << MJIN2_STR("key press. key: %d unmodifiedKey: %d\n",
                                   ea.getKey(),
                                   ea.getUnmodifiedKey());
#endif
            InputEvent e(ea.getKey(),
                         ea.getUnmodifiedKey(),
                         x,
                         y,
                         yi,
                         true);
            for (auto it = mListeners.begin(); it != mListeners.end(); ++it)
                it->first->onInput(e);
            return catchAll;
        }
        case osgGA::GUIEventAdapter::KEYUP:
        {
#ifdef MJIN2_DEBUG_INPUT
            std::cout << MJIN2_STR("key release. key: %d unmodifiedKey: %d\n",
                                   ea.getKey(),
                                   ea.getUnmodifiedKey());
#endif
            InputEvent e(ea.getKey(),
                         ea.getUnmodifiedKey(),
                         x,
                         y,
                         yi,
                         false);
            for (auto it = mListeners.begin(); it != mListeners.end(); ++it)
                it->first->onInput(e);
            return catchAll;
        }
        case osgGA::GUIEventAdapter::MOVE:
        {
#ifdef MJIN2_DEBUG_INPUT
            std::cout << MJIN2_STR("mouse move. x: %f y: %f\n", x, y);
#endif
            InputEvent e(INPUT_MOUSE_MOVE,
                         INPUT_MOUSE_MOVE,
                         x,
                         y,
                         yi,
                         true);
            for (auto it = mListeners.begin(); it != mListeners.end(); ++it)
                it->first->onInput(e);
            return catchAll;
        }
        case osgGA::GUIEventAdapter::PUSH:
        {
#ifdef MJIN2_DEBUG_INPUT
            std::cout << MJIN2_STR("mouse press. button: %d x: %f y: %f\n",
                                   ea.getButton(),
                                   x,
                                   y);
#endif
            InputEvent e(mouseButton(ea.getButton()),
                         mouseButton(ea.getButton()),
                         x,
                         y,
                         yi,
                         true);
            for (auto it = mListeners.begin(); it != mListeners.end(); ++it)
                it->first->onInput(e);
            return catchAll;
        }
        case osgGA::GUIEventAdapter::RELEASE:
        {
#ifdef MJIN2_DEBUG_INPUT
            std::cout << MJIN2_STR("mouse release. button: %d x: %f y: %f\n",
                                   ea.getButton(),
                                   x,
                                   y);
#endif
            InputEvent e(mouseButton(ea.getButton()),
                         mouseButton(ea.getButton()),
                         x,
                         y,
                         yi,
                         false);
            for (auto it = mListeners.begin(); it != mListeners.end(); ++it)
                it->first->onInput(e);
            return catchAll;
        }
        case osgGA::GUIEventAdapter::SCROLL:
        {
#ifdef MJIN2_DEBUG_INPUT
            std::cout << MJIN2_STR("mouse scroll. scrolling motion: %u\n",
                                   ea.getScrollingMotion());
#endif
            InputEvent e(scroll(ea.getScrollingMotion()),
                         scroll(ea.getScrollingMotion()),
                         x,
                         y,
                         yi,
                         true);
            for (auto it = mListeners.begin(); it != mListeners.end(); ++it)
                it->first->onInput(e);
            return catchAll;
        }
        default:
            break;
    }
    return false;
}

void Input::removeListener(InputListener *listener) throw ()
{
    mListeners.erase(listener);
}

void Input::setWindowHeight(u32 height)
{
    mWindowHeight = height;
}

s32 Input::mouseButton(s32 mb)
{
    switch (mb)
    {
        case osgGA::GUIEventAdapter::LEFT_MOUSE_BUTTON:
            return INPUT_MOUSE_BUTTON_LEFT;
        case osgGA::GUIEventAdapter::MIDDLE_MOUSE_BUTTON:
            return INPUT_MOUSE_BUTTON_MIDDLE;
        case osgGA::GUIEventAdapter::RIGHT_MOUSE_BUTTON:
            return INPUT_MOUSE_BUTTON_RIGHT;
        default:
            return INPUT_MOUSE_BUTTON_NONE;
    }
}

INPUT Input::scroll(osgGA::GUIEventAdapter::ScrollingMotion motion)
{
    switch (motion)
    {
        case osgGA::GUIEventAdapter::SCROLL_LEFT:
            return INPUT_SCROLL_LEFT;
        case osgGA::GUIEventAdapter::SCROLL_RIGHT:
            return INPUT_SCROLL_RIGHT;
        case osgGA::GUIEventAdapter::SCROLL_UP:
            return INPUT_SCROLL_UP;
        case osgGA::GUIEventAdapter::SCROLL_DOWN:
            return INPUT_SCROLL_DOWN;
        default:
            return INPUT_SCROLL_NONE;
    }
}

} // namespace priv
} // namespace mjin2

